Google App EngineでGoogle Cloud SQLを使ってみる
Google Cloud SQLとは
先日、Googleは同社が提供するクラウドサービスであるGoogle App Engine ※1でリレーショナルDBが使用できるサービス、 Googel Cloud SQL ※2を発表しました。このサービスはMySQLライクな機能を提供し、DC間の同期もサポートされているようです。 Google Cloud SQLは2011/10/14現在、Labsにて限定プレビューになっています。
Google App EngineのDBといえば、いままではNoSQLデータベースであるBigTableを使用するしかありませんでしたが、 今後はMySQLライクなリレーショナルDBを(もしくは両方同時に)使用することができるようになります。 現在は限定プレビューなので、いくつか手順を踏まなければ使用することができませんが、ご紹介させて頂きます。
Google Cloud SQLを動かす
ここにGoogle Cloud SQLを動かすまでの詳細な手順があります。 これにそってやっていきましょう。
1.限定プレビューに登録
まずは限定プレビューへの参加を行います。 Google API Consoleにアクセスし、servicesタブからGoogle Cloud SQLを探しましょう。
Request accessをクリックし、どの程度のサイズを使用するか等の必要事項を入力して申請します。 申請してからしばらく(数日?)すると、登録したメールアドレス宛に招待メールが送られてきます。 メールにあるリンクをクリックして使用条件に同意すればGoogle Cloud SQLを使い始めることができます。
2.Google App Engine SDKをインストール
Google Cloud SQLは現在GAEアプリからのみアクセス可能なので、GAEアプリの開発環境を用意します。 ここからSDKかEclipse pluginをダウンロードしてセットアップしておきましょう。 今回はJavaで、Google Eclipse pluginを使用して動作確認をしました。
3.GAEアプリの雛形作成
Google Cloud SQLはインスタンス作成時にアクセス可能なGAEのApplicationIDを指定します。 後で変更も可能ですが、インスタンス作成時に指定してしまいたいので、まずはGAEアプリの雛形を作成してしまいましょう。 Eclipseを起動し、File > new > Web Application Projectを選択し、適当な名前をつけてGAE用プロジェクトを作成してください。 作成が完了したら、appengine-web.xmlに書いてあるApplicationIDを覚えておいてください。
4.Google Cloud SQLインスタンスを作成
使用準備ができたら、Google APIs Consoleにアクセスします。 ドロップダウンでGoogle Cloud SQLのプロジェクトが出ているのを確認し、左のメニューからGoogle Cloud SQLをクリックします。 なにもインスタンスがない状態なので、Create new instanceをクリックしましょう。 ダイアログが起動するので、インスタンス名と先ほど作成したGAEアプリのApplicationIDを入力してインスタンスを作成します。
5.SQL PromptでSQLを実行
Google APIs Consoleで先程作成したインスタンスを選択し、「SQL Prompt」のタブを選択します。 ここでは直接SQLを実行することができるので、データベースの作成やテーブルの作成など、初期設定を行います。 まずはデータベースを作成しましょう。
create database mycloud;
続いてテスト用テーブルを1つ作成します。 ドロップダウンにいま作成したデータベースを選択して、次のSQLを実行してください。
create table user (name VARCHAR(255),id INT NOT NULL AUTO_INCREMENT, PRIMARY KEY(id));
シンプルなuserテーブルを作成します。 これでデータベースの準備ができました。次にGAEアプリからアクセスするためのプログラムを記述します。
6.プログラムを記述する
今回はJSPから名前を入力してボタンを押下するとユーザーがuserテーブルに登録され、同一のJSPページに全ユーザーが表示されるだけのシンプルなアプリを作成します。 まずはサーブレットを作成しましょう。下記が作成するサーブレット全文です。
package jp.cm.mycouldsqltest; import java.io.IOException; import java.io.PrintWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.google.appengine.api.rdbms.AppEngineDriver; @SuppressWarnings("serial") public class MySqlTestServlet extends HttpServlet { private static final String DB_URL = "jdbc:google:rdbms://インスタンス名/データベース名"; private static final String INSERT_SQL = "INSERT INTO user (name) VALUES(?)"; @Override public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException { PrintWriter out = resp.getWriter(); Connection c = null; try { DriverManager.registerDriver(new AppEngineDriver()); c = DriverManager.getConnection(DB_URL); String name = req.getParameter("name"); if (name == "") { out.println("<html><head></head><body>You are missing either a message or a name! Try again! Redirecting in 3 seconds...</body></html>"); } else { String statement = INSERT_SQL; PreparedStatement stmt = c.prepareStatement(statement); stmt.setString(1, name); int success = 2; success = stmt.executeUpdate(); if (success == 1) { out.println("<html><head></head><body>Success! Redirecting in 3 seconds...</body></html>"); } else if (success == 0) { out.println("<html><head></head><body>Failure! Please try again! Redirecting in 3 seconds...</body></html>"); } } } catch (SQLException e) { e.printStackTrace(); } finally { if (c != null) try { c.close(); } catch (SQLException ignore) { } } resp.setHeader("Refresh", "3; url=/mysqltest.jsp"); } }
特徴的なのは、コネクションを取得するところです。(28〜29行目付近) java.sql.DriverManagerに対してcom.google.appengine.api.rdbms.AppEngineDriverを渡しています。 コネクションを取得するURLは、
jdbc:google:rdbms://インスタンス名/データベース名
としています。忘れてしまった人は、もう一度Goolge APIs Consoleで確認してください。 コネクション取得以外は通常のJDBCプログラミングと同じです。
次にwarディレクト直下にmysqltest.jspを作成します。
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ page import="java.util.List" %> <%@ page import="java.sql.*" %> <%@ page import="com.google.appengine.api.rdbms.AppEngineDriver" %> <html> <body> <% Connection c = null; c = DriverManager.getConnection("jdbc:google:rdbms://インスタンス名/データベース名"); ResultSet rs = c.createStatement().executeQuery("SELECT name, id FROM user"); %> <table style="border: 1px solid black"> <tbody> <tr> <th width="35%" style="background-color: #CCFFCC; margin: 5px">Name</th> <th style="background-color: #CCFFCC; margin: 5px">Message</th> <th style="background-color: #CCFFCC; margin: 5px">ID</th> </tr> <% while (rs.next()){ String name = rs.getString("name"); int id = rs.getInt("id"); %> <tr> <td><%= name %></td> <td><%= id %></td> </tr> <% } c.close(); %> </tbody> </table> <br /> No more messages! <p><strong>entry user.</strong></p> <form action="/mysqltest" method="post"> <div>First Name: <input type="text" name="name"></input></div> <div><input type="submit" value="Post" /></div> </form> </body> </html>
ユーザー検索はJSPで直接実行しています。画面上部にユーザー一覧を表示し、ユーザー登録用フォームを表示します。 名前を入力してボタンを押下すると、さきほどのサーブレットで登録処理が実行されます。
最後にweb.xmlでサーブレットの登録を記述します。
<?xml version="1.0" encoding="utf-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <servlet> <servlet-name>MySqlTest</servlet-name> <servlet-class>jp.cm.mycouldsqltest.MySqlTestServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MySqlTest</servlet-name> <url-pattern>/mysqltest</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>mysqltest.jsp</welcome-file> </welcome-file-list> </web-app>
記述したら、Google plugin for EclipseのAppDeploy to App Engineを選択して、GAEにデプロイします。 デプロイが成功したら、 http://<ApplicationID>.appspot.com/mysqltest.jsp にアクセスしてみてください。下記のような画面が表示され、DBへのアクセスができます。
まとめ
今回はGAEでリレーショナルDB(MySQL)を使用するためのサービス、Google CLoud SQLを少しだけ試してみました。 いままではGAEの特徴だったBigTableがネックとなり、GAEの採用にいまいち踏み切れなかったケースがあったかもしれませんが、 リレーショナルDBが使用できるようになることで選択の幅が広がるようになるかもしれません。